contents
REST(REpresentational State Transfer)는 프로토콜이나 표준이 아닌, 네트워크 애플리케이션 설계를 위한 아키텍처 스타일입니다. RESTful API란 이 스타일의 제약 조건을 준수하는 API를 말합니다. RESTful API를 설계한다는 것은 데이터와 서비스를 위한 예측 가능하고, 논리적이며, 사용하기 쉬운 인터페이스를 만드는 것입니다.
REST의 기본 원칙 (제약 조건) 🏛️
구성 요소를 살펴보기 전에 먼저 그 철학을 이해해야 합니다. API는 다음 여섯 가지 기본 원칙을 따라야만 "RESTful"하다고 할 수 있습니다.
-
클라이언트-서버 구조 (Client-Server Architecture): 클라이언트(데이터를 요청하는 쪽)와 서버(데이터를 보유한 쪽)는 완전히 분리됩니다. 이러한 관심사의 분리는 클라이언트와 서버가 서로 독립적으로 발전할 수 있게 합니다.
-
무상태성 (Stateless): 가장 중요한 제약 조건입니다. 클라이언트에서 서버로 보내는 모든 요청은 그 요청을 이해하고 완료하는 데 필요한 모든 정보를 포함해야 합니다. 서버는 요청과 요청 사이에 클라이언트의 상태 정보를 저장하지 않습니다.
- 비유: 자판기를 생각해 보세요. 자판기와 상호작용할 때마다 그것은 새롭고 완전한 거래입니다. 기계는 당신의 이전 구매 기록을 기억하지 않습니다. 이는 시스템을 더 신뢰할 수 있고, 가시적이며, 확장성 있게 만듭니다.
-
캐시 가능 (Cacheable): 서버의 응답은 캐시가 가능한지 아닌지를 명시해야 합니다. 이를 통해 클라이언트나 중간 프록시가 응답을 캐싱하여 성능과 확장성을 크게 향상시킬 수 있습니다.
-
일관된 인터페이스 (Uniform Interface): REST의 핵심으로, API가 일관되고 표준화된 방식으로 설계되도록 보장합니다. 여기에는 네 가지 하위 제약 조건이 있습니다.
-
자원의 식별 (Resource Identification): 모든 "사물"이나 개념은 **자원(Resource)**이며, **URI(Uniform Resource Identifier)**라는 안정적이고 고유한 식별자로 식별됩니다.
-
표현을 통한 자원의 조작 (Manipulation of Resources Through Representations): 클라이언트는 자원에 직접 상호작용하는 것이 아니라, 자원의 표현(representation)(주로 JSON 객체)과 상호작용합니다. 클라이언트는 표준 HTTP 동사(GET, POST, DELETE 등)를 사용하여 이 자원들을 조작합니다.
-
자기 서술적 메시지 (Self-Descriptive Messages): 각 메시지(요청/응답)는 수신자가 그 메시지를 이해하는 데 충분한 정보를 포함합니다. 예를 들어,
Content-Type: application/json같은 HTTP 헤더는 수신자에게 메시지 본문을 어떻게 파싱해야 하는지 알려줍니다. -
HATEOAS (Hypermedia as the Engine of Application State): 가장 발전된 원칙입니다. 서버의 응답에 클라이언트가 다음에 수행할 수 있는 작업들을 알려주는 링크(하이퍼미디어)가 포함되어야 함을 의미합니다. 클라이언트는 마치 웹사이트를 둘러보듯 이 링크들을 따라가며 API를 탐색합니다.
-
-
계층화된 시스템 (Layered System): 클라이언트는 최종 서버에 직접 연결되었는지, 아니면 중간의 다른 서버(로드 밸런서, 캐시, 프록시 등)에 연결되었는지 보통 알 수 없습니다. 이는 더 나은 확장성과 보안을 가능하게 합니다.
-
코드 온디맨드 (Code-On-Demand, 선택 사항): 서버는 실행 가능한 로직(예: 자바스크립트 코드)을 클라이언트에 전송하여 일시적으로 클라이언트의 기능을 확장하거나 맞춤 설정할 수 있습니다.
REST API 설계의 핵심 구성 요소
API를 설계할 때 주로 다음 다섯 가지 구성 요소를 다루게 됩니다.
1. 자원 (Resources - 명사)
자원은 모든 REST API의 기본 개념입니다. 상태를 가지고 있고 조작될 수 있는 "무언가"의 객체 또는 표현입니다.
-
자원이란? 사용자, 제품, 주문, 사진 등 노출하고자 하는 모든 개념입니다.
-
이름 규칙: 동사가 아닌 명사를 사용합니다. 행위가 아닌 사물을 식별하는 것입니다.
-
복수형: 일관성을 유지하세요. 자원의 컬렉션을 나타내기 위해 복수형을 사용하는 것이 일반적인 규칙입니다.
-
좋은 예:
/products,/users,/orders -
나쁜 예:
/getAllProducts,/createNewUser
-
2. URI (Uniform Resource Identifier - 주소)
URI는 자원을 식별하는 데 사용되는 주소입니다. 잘 설계된 URI 구조는 예측 가능하고 개발자가 이해하기 쉽습니다.
-
구조: URI는 계층적이고 논리적인 구조를 따라야 합니다.
-
컬렉션: 자원의 모음(예: 모든 제품)을 식별합니다.
https://api.example.com/products
-
특정 항목: 컬렉션 내의 단일 자원을 식별합니다.
https://api.example.com/products/12345
-
중첩된 자원: 명확한 부모-자식 관계가 있는 자원에 사용합니다.
-
https://api.example.com/users/55/orders(55번 사용자의 모든 주문 조회) -
https://api.example.com/users/55/orders/99(55번 사용자의 99번 주문 조회)
-
3. HTTP 동사 (Verbs - 행위) 🚦
URI에 행위를 넣지 않습니다. 대신, URI로 식별된 자원을 조작하기 위해 표준 HTTP 메서드를 사용합니다.
| 동사 | 행위 | 설명 | 안전(Safe)? | 멱등성(Idempotent)?* |
|---|---|---|---|---|
| GET | 읽기 | 자원 또는 자원 컬렉션의 표현을 검색합니다. | 예 | 예 |
| POST | 생성 | 새로운 자원을 생성합니다. 주로 컬렉션 URI(/products)에 사용됩니다. |
아니요 | 아니요 |
| PUT | 수정/대체 | 기존 자원 전체를 새로운 표현으로 대체합니다. | 아니요 | 예 |
| PATCH | 부분 수정 | 자원에 부분적인 수정을 적용합니다. | 아니요 | 아니요** |
| DELETE | 삭제 | 자원을 제거합니다. | 아니요 | 예 |
-
안전(Safe): 해당 연산이 서버의 상태를 변경하지 않음을 의미합니다.
GET은 안전합니다. -
멱등성(Idempotent): 동일한 요청을 여러 번 보내도 한 번 보낸 것과 동일한 효과를 가짐을 의미합니다. 123번 자원을 두 번 삭제하는 것은 한 번 삭제하는 것과 같습니다.
4. 표현 (Representations - 데이터 포맷) 📦
자원은 클라이언트와 서버 간에 특정 포맷, 즉 표현을 사용하여 교환됩니다.
-
JSON (JavaScript Object Notation): 현대 API의 압도적인 표준입니다. 가볍고, 사람이 읽기 쉬우며, 기계가 파싱하기 쉽습니다.
-
콘텐츠 협상 (Content Negotiation): REST는 클라이언트와 서버가 포맷을 협상하도록 허용합니다. 클라이언트는 원하는 것을 알리기 위해
Accept: application/json헤더를 보내고, 서버는 보내는 것의 종류를 알리기 위해Content-Type: application/json헤더를 보냅니다.
5. 상태 코드 (Status Codes - 서버의 응답)
서버는 요청의 결과를 알리기 위해 표준 HTTP 상태 코드를 사용합니다. 이를 통해 클라이언트는 작업이 성공했는지, 실패했는지 또는 다른 상황이 발생했는지 즉시 알 수 있습니다.
-
2xx (성공):
-
200 OK:GET,PUT,PATCH에 대한 표준 성공 응답. -
201 Created: 새로운 자원이 성공적으로 생성됨 (POST). -
204 No Content: 요청은 성공했지만 반환할 데이터가 없음 (주로DELETE에 사용).
-
-
3xx (리다이렉션):
301 Moved Permanently: 자원의 URI가 영구적으로 변경됨.
-
4xx (클라이언트 오류): 클라이언트 측의 잘못.
-
400 Bad Request: 요청이 잘못됨 (예: 유효하지 않은 JSON). -
401 Unauthorized: 클라이언트가 인증되지 않음. -
403 Forbidden: 클라이언트는 인증되었지만 이 작업을 수행할 권한이 없음. -
404 Not Found: 요청한 자원이 존재하지 않음.
-
-
5xx (서버 오류): 서버가 유효한 요청을 처리하는 데 실패함.
500 Internal Server Error: 서버에서 발생한 일반적인 예기치 않은 오류.
종합 예시: 실제 적용 ✅
블로그 **게시물(posts)**을 위한 간단한 API를 설계해 보겠습니다.
-
GET /posts: 모든 게시물 목록을 검색합니다. (
200 OK반환) -
GET /posts/123: ID가 123인 게시물을 검색합니다. (
200 OK또는404 Not Found반환) -
POST /posts: 새 게시물을 생성합니다. 요청 본문에는 게시물 데이터(제목, 내용)가 포함됩니다. 서버가 ID를 할당합니다. (새 게시물 데이터와
/posts/{new_id}를 가리키는Location헤더와 함께201 Created반환) -
PUT /posts/123: 123번 게시물 전체를 요청 본문의 새 데이터로 대체합니다. (
200 OK반환) -
PATCH /posts/123: 123번 게시물을 부분적으로 수정합니다. 요청 본문에는
{"title": "새로운 제목"}만 포함될 수 있습니다. (200 OK반환) -
DELETE /posts/123: 123번 게시물을 삭제합니다. (
204 No Content반환)
모범 사례 및 고급 개념
-
버전 관리 (Versioning): 기존 클라이언트를 손상시키지 않고 변경할 수 있도록 항상 API 버전을 관리하세요. URI에 버전을 포함하는 것이 일반적입니다:
https://api.example.com/v1/products. -
필터링, 정렬, 페이지네이션 (Filtering, Sorting, and Pagination): 컬렉션의 경우, 클라이언트가 쿼리 파라미터를 통해 결과를 세분화할 수 있도록 허용하세요.
-
GET /posts?status=published(필터링) -
GET /posts?sort=-created_at(정렬) -
GET /posts?limit=10&offset=20(페이지네이션)
-
-
오류 처리 (Error Handling): 응답 본문에 의미 있는 오류 메시지를 제공하세요.
JSON
{ "error": "InvalidEmail", "message": "제공된 이메일 주소는 유효한 형식이 아닙니다." } -
인증 (Authentication): OAuth 2.0과 같은 표준을 사용하여 API를 보호하세요. 토큰은 일반적으로
Authorization헤더를 통해 전송됩니다.
references